package com.gitee.wsl.mathematics.matrix.mat4.ext


import com.gitee.wsl.mathematics.matrix.mat4.Matrix4
import com.gitee.wsl.mathematics.matrix.mat4.MutableMatrix4
import com.gitee.wsl.mathematics.vector.vec4.Vector4
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking

infix operator fun<T:Number> Matrix4<T, *, *>.times(a: Vector4<T,*>) = times( a.x, a.y, a.z, a.w)

/** Vec4 row = Vec4 col * Mat4  */
fun<T:Number> Matrix4<T, *, *>.times( a0: T, a1: T, a2: T, a3: T) = createVec4 { res->
    res[0] = a0 * this[0, 0] + a1 * this[0, 1] + a2 * this[0, 2] + a3 * this[0, 3]
    res[1] = a0 * this[1, 0] + a1 * this[1, 1] + a2 * this[1, 2] + a3 * this[1, 3]
    res[2] = a0 * this[2, 0] + a1 * this[2, 1] + a2 * this[2, 2] + a3 * this[2, 3]
    res[3] = a0 * this[3, 0] + a1 * this[3, 1] + a2 * this[3, 2] + a3 * this[3, 3]
}

fun<T:Number,V:Vector4<T,V>,M:Matrix4<T,M,V>> CoroutineScope.mul(a:M, other: Matrix4<T, *, *>):M{
   a.run {
        fun f(x: Int, y: Int) = async {
                a[0, y] * other[x, 0] + a[1, y] * other[x, 1] + a[2, y] * other[x, 2] + a[3, y] * other[x, 3]
        }
       return runBlocking {
            create(
               f(0, 0).await(), f(1, 0).await(), f(2, 0).await(),f(3, 0).await(),
               f(0, 1).await(), f(1, 1).await(), f(2, 1).await(),f(3, 1).await(),
               f(0, 2).await(), f(1, 2).await(), f(2, 2).await(),f(3, 2).await(),
               f(0, 2).await(), f(1, 2).await(), f(2, 2).await(),f(3, 3).await()
           )
       }
    }

}

operator fun<T:Number> Matrix4<T, *, *>.times(other: Matrix4<T, *, *>): Matrix4<T, *, *> {

        fun f(x: Int, y: Int) = this[0, y] * other[x, 0] + this[1, y] * other[x, 1] + this[2, y] * other[x, 2] + this[3, y] * other[x, 3]

        return create(
                f(0, 0), f(1, 0), f(2, 0),f(3, 0),
                f(0, 1), f(1, 1), f(2, 1),f(3, 1),
                f(0, 2), f(1, 2), f(2, 2),f(3, 2),
                f(0, 2), f(1, 2), f(2, 2),f(3, 3)
            ) as Matrix4<T, *, *>
    }

@Suppress("NOTHING_TO_INLINE")  // it does make a difference (~25% faster)
internal inline fun<T:Number> MutableMatrix4<T, *, *>.mul(
    t00: T, t01: T, t02: T, t03: T,
    t10: T, t11: T, t12: T, t13: T,
    t20: T, t21: T, t22: T, t23: T,
    t30: T, t31: T, t32: T, t33: T
){
    val r00 = m00 * t00 + m01 * t10 + m02 * t20 + m03 * t30
    val r10 = m10 * t00 + m11 * t10 + m12 * t20 + m13 * t30
    val r20 = m20 * t00 + m21 * t10 + m22 * t20 + m23 * t30
    val r30 = m30 * t00 + m31 * t10 + m32 * t20 + m33 * t30

    val r01 = m00 * t01 + m01 * t11 + m02 * t21 + m03 * t31
    val r11 = m10 * t01 + m11 * t11 + m12 * t21 + m13 * t31
    val r21 = m20 * t01 + m21 * t11 + m22 * t21 + m23 * t31
    val r31 = m30 * t01 + m31 * t11 + m32 * t21 + m33 * t31

    val r02 = m00 * t02 + m01 * t12 + m02 * t22 + m03 * t32
    val r12 = m10 * t02 + m11 * t12 + m12 * t22 + m13 * t32
    val r22 = m20 * t02 + m21 * t12 + m22 * t22 + m23 * t32
    val r32 = m30 * t02 + m31 * t12 + m32 * t22 + m33 * t32

    val r03 = m00 * t03 + m01 * t13 + m02 * t23 + m03 * t33
    val r13 = m10 * t03 + m11 * t13 + m12 * t23 + m13 * t33
    val r23 = m20 * t03 + m21 * t13 + m22 * t23 + m23 * t33
    val r33 = m30 * t03 + m31 * t13 + m32 * t23 + m33 * t33

    m00 = r00; m01 = r01; m02 = r02; m03 = r03
    m10 = r10; m11 = r11; m12 = r12; m13 = r13
    m20 = r20; m21 = r21; m22 = r22; m23 = r23
    m30 = r30; m31 = r31; m32 = r32; m33 = r33
}


@Suppress("NOTHING_TO_INLINE")  // it does make a difference (~25% faster)
internal inline fun<T:Number> MutableMatrix4<T, *, *>.mul33(
    t00: T, t01: T, t02: T,
    t10: T, t11: T, t12: T,
    t20: T, t21: T, t22: T
) {
    val r00 = m00 * t00 + m01 * t10 + m02 * t20
    val r10 = m10 * t00 + m11 * t10 + m12 * t20
    val r20 = m20 * t00 + m21 * t10 + m22 * t20
    val r30 = m30 * t00 + m31 * t10 + m32 * t20

    val r01 = m00 * t01 + m01 * t11 + m02 * t21
    val r11 = m10 * t01 + m11 * t11 + m12 * t21
    val r21 = m20 * t01 + m21 * t11 + m22 * t21
    val r31 = m30 * t01 + m31 * t11 + m32 * t21

    val r02 = m00 * t02 + m01 * t12 + m02 * t22
    val r12 = m10 * t02 + m11 * t12 + m12 * t22
    val r22 = m20 * t02 + m21 * t12 + m22 * t22
    val r32 = m30 * t02 + m31 * t12 + m32 * t22

    m00 = r00; m01 = r01; m02 = r02
    m10 = r10; m11 = r11; m12 = r12
    m20 = r20; m21 = r21; m22 = r22
    m30 = r30; m31 = r31; m32 = r32
}

